package net.xerllent.security;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import javax.servlet.FilterConfig;

import org.apache.log4j.Logger;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * @author sxh
 * @date 
 * @describe 安全过滤配置管理类，由XSSSecurityManger修改
 */
public class XSSSecurityManager {
	
	private static Logger logger = Logger.getLogger(XSSSecurityManager.class);
	
	/**
	 * REGEX：校验正则表达式
	 */
	public static String REGEX;
	public static Map<String,Set<String>> EXCLUDEMAP;//需要忽略的url及对应的参数列表
	public static Set<String> EXTENTIONS;//需要过滤的扩展名
	
	 /**
     * 特殊字符匹配
     */
    private static Pattern XSS_PATTERN ;
	
    
    private XSSSecurityManager(){
        //不可被实例化
    }
    
    public static void init(FilterConfig config){
    	logger.info("XSSSecurityManager init(FilterConfig config) begin");
    	//初始化过滤配置文件
        String xssPath = config.getServletContext().getRealPath("/") + config.getInitParameter("securityconfig");
        
        // 初始化安全过滤配置
        try {
			if(initConfig(xssPath)){
				// 生成匹配器
				XSS_PATTERN = Pattern.compile(".*"+REGEX+".*",Pattern.CASE_INSENSITIVE|Pattern.MULTILINE);
			}
		} catch (DocumentException e) {
			logger.error("安全过滤配置文件xss_security_config.xml加载异常",e);
		}
		logger.info("XSSSecurityManager init(FilterConfig config) end");
    }
    
    /**
     * 读取安全审核配置文件xss_security_config.xml
     * 设置XSSSecurityConfig配置信息
     * @param path 配置文件地址 eg C:/apache-tomcat-6.0.33/webapps/security_filter/WebRoot/config/xss/xss_security_config.xml
     * @return 
     * @throws DocumentException
     */
	@SuppressWarnings("unchecked")
	public static boolean initConfig(String path) throws DocumentException {
		logger.info("XSSSecurityManager.initConfig(String path) begin");
		Element superElement = new SAXReader().read(path).getRootElement();
		XSSSecurityConfig.IS_CHECK_HEADER = new Boolean(getEleValue(superElement,XSSSecurityCon.IS_CHECK_HEADER));
		XSSSecurityConfig.IS_CHECK_PARAMETER = new Boolean(getEleValue(superElement,XSSSecurityCon.IS_CHECK_PARAMETER));
		XSSSecurityConfig.IS_LOG = new Boolean(getEleValue(superElement,XSSSecurityCon.IS_LOG));
		XSSSecurityConfig.IS_CHAIN = new Boolean(getEleValue(superElement,XSSSecurityCon.IS_CHAIN));
		XSSSecurityConfig.REPLACE = new Boolean(getEleValue(superElement,XSSSecurityCon.REPLACE));

		//内容检测正则表达式生成
		Element regexEle = superElement.element(XSSSecurityCon.REGEX_LIST);
		if(regexEle != null){
			Iterator<Element> regexIt = regexEle.elementIterator();
			StringBuffer tempStr = new StringBuffer();
			//xml的cdata标签传输数据时，会默认在\前加\，需要将\\替换为\
			while(regexIt.hasNext()){
				if(tempStr.length()>0)tempStr.append("|");
				Element regex = (Element)regexIt.next();
				String tmp = regex.getText();
				tmp = tmp.replaceAll("\\\\\\\\", "\\\\");
	        	tempStr.append(tmp);
			}
			REGEX= tempStr.toString();
			logger.info("安全匹配规则"+REGEX);
		}else{
			logger.error("安全过滤配置文件中没有 "+XSSSecurityCon.REGEX_LIST+" 属性");
			return false;
		}
		
		//扩展名包含Set生成
		Element includeEle = superElement.element(XSSSecurityCon.INCLUDE_LIST);
		if(includeEle != null){
			Iterator<Element> includeIt = includeEle.elementIterator();
			while(includeIt.hasNext()){
				Element item = (Element)includeIt.next();
				String ext = item.getText();
				if(ext!=null&&ext.length()>0){
					ext=ext.trim();
					if(ext.equals("*")||ext.equals("*.*")){EXTENTIONS=null;break;}//匹配所有则设置EXTENTIONS=null
					ext=ext.replaceAll("\\*", "");//删除所有*,例如*.jsp则留下.jsp
					if(ext.length()>0){
						if(EXTENTIONS==null){EXTENTIONS=new HashSet<String>();}
						EXTENTIONS.add(ext);
					}
				}
			}
		}

		
		//忽略url及参数map生成
		Element excludeEle = superElement.element(XSSSecurityCon.EXCLUDE_LIST);
		if(excludeEle != null){
			Iterator<Element> excludeIt = excludeEle.elementIterator();
			while(excludeIt.hasNext()){
				Element item = (Element)excludeIt.next();
				String url = getEleValue(item,XSSSecurityCon.URL);
				if(url!=null&&url.length()>0){
					if(EXCLUDEMAP==null){EXCLUDEMAP=new HashMap<String, Set<String>>();}
					
					Set<String> params=null;
					Element paramsELe = item.element(XSSSecurityCon.PARAMS);
					if(paramsELe !=null){
						Iterator<Element> paramIt = paramsELe.elementIterator();
						while(paramIt.hasNext()){
							Element param = (Element)paramIt.next();
							String name = param.getText();
							if(name!=null&&name.length()>0){
								if(params==null){params=new HashSet<String>();}
								params.add(name.trim());
							}
						}
					}
					
					EXCLUDEMAP.put(url, params);
				}
			}
		}
		
		logger.info("XSSSecurityManager.initConfig(String path) end");
		return true;

	}
    
	/**
	 * 从目标element中获取指定标签信息，若找不到该标签，记录错误日志
	 * @param element 目标节点
	 * @param tagName 制定标签
	 * @return 
	 */
	private static String getEleValue(Element element, String tagName){
		String tmp=element.elementText(tagName);
		return tmp==null? null:element.elementText(tagName).trim();
	}
	
    /**
     * 对非法字符进行替换
     * @param text
     * @return
     */
    public static String securityReplace(String text){
    	if(isNullStr(text)){
    		return text;
    	}else{
    		return text.replaceAll(REGEX, XSSSecurityCon.REPLACEMENT);
    	}
    }
    
    /**
     * 匹配字符是否含特殊字符
     * @param text
     * @return
     */
    public static boolean matches(String text){
    	if(text==null){
    		return false;
    	}
    	return XSS_PATTERN.matcher(text).matches();
    }
    
    /**
     * 释放关键信息
     */
    public static void destroy(){
    	logger.info("XSSSecurityManager.destroy() begin");
        XSS_PATTERN = null;
        REGEX = null;
        logger.info("XSSSecurityManager.destroy() end");
    }
    
    /**
     * 判断是否为空串，建议放到某个工具类中
     * @param value
     * @return
     */
    public static boolean isNullStr(String value){
    	return value == null || value.trim().equals("");
    }
}
